Path: blob/master/webhooks/webhook_preview/app/[uuid]/page.tsx
640 views
"use client";12import { useState, useEffect } from "react";3import { Header } from "@/components/header";4import { Layout } from "@/components/layout";5import { LicensePlateSidebar } from "@/components/license-plate-sidebar";6import { LastVehicleDetails } from "@/components/last-vehicle-details";7import { DashboardSummary } from "@/components/dashboard-summary";8import { EmptyState } from "@/components/empty-state";9import type { WebhookData } from "@/types/webhook";10import { useToast } from "@/hooks/use-toast";11import { useParams, useRouter } from "next/navigation";12import { Copy, Trash } from "lucide-react";1314export default function WebhookDataPage() {15const params = useParams();16const uuid = params?.uuid as string;1718const [webhookData, setWebhookData] = useState<WebhookData[]>([]);19const [loading, setLoading] = useState(true);20const [searchTerm, setSearchTerm] = useState("");21const { toast } = useToast();22const router = useRouter();23const [baseUrl, setBaseUrl] = useState("");2425// Set base URL on client side26useEffect(() => {27setBaseUrl(window.location.origin);28}, []);2930// Fetch data on page load31useEffect(() => {32if (!uuid) return;3334const fetchData = async () => {35try {36setLoading(true);37const url = `/api/webhook/${uuid}`;38const response = await fetch(url);3940if (!response.ok) {41if (response.status === 404) {42toast({43title: "Webhook Not Found",44description: "The webhook you're looking for doesn't exist",45variant: "destructive",46});47router.push("/");48return;49} else if (response.status === 400) {50toast({51title: "Invalid UUID",52description: "The UUID you provided is invalid",53variant: "destructive",54});55router.push("/");56return;57}5859toast({60title: "Error",61description: `Failed to fetch data: ${response.statusText}`,62variant: "destructive",63});64return;65}6667const data = await response.json();68if (data.length >= process.env.NEXT_PUBLIC_MAX_WEBHOOK_REQUESTS) {69toast({70title: "Webhook Limit Reached",71description:72"You have reached the maximum number of requests for this webhook",73variant: "destructive",74});75}7677if (Array.isArray(data) && data.length > 0) {78console.log("Received data:", data.length, "items");79setWebhookData(data);80} else {81setWebhookData([]);82}83} catch (error) {84console.error("Error fetching webhook data:", error);85toast({86title: "Error",87description: "Failed to fetch webhook data",88variant: "destructive",89});90} finally {91setLoading(false);92}93};9495// Fetch initial data96fetchData();9798// Set up polling for real-time updates99const intervalId = setInterval(fetchData, 5000);100101return () => clearInterval(intervalId);102}, [uuid, toast, router]);103104const handleRefresh = async () => {105setLoading(true);106try {107const response = await fetch(`/api/webhook/${uuid}`);108if (response.ok) {109const data = await response.json();110setWebhookData(data);111toast({112title: "Data Refreshed",113description: `Found ${data.length} records`,114});115}116} catch (error) {117console.error("Error refreshing data:", error);118toast({119title: "Refresh Failed",120description: "Could not refresh data",121variant: "destructive",122});123} finally {124setLoading(false);125}126};127128const handleDataClear = async () => {129try {130setLoading(true);131const response = await fetch(`/api/webhook/${uuid}`, {132method: "DELETE",133});134135if (response.ok) {136setWebhookData([]);137toast({138title: "Data Cleared",139description: "All webhook data has been cleared",140});141} else {142toast({143title: "Error",144description: "Failed to clear data",145variant: "destructive",146});147}148} catch (error) {149console.error("Error clearing data:", error);150toast({151title: "Error",152description: "Failed to clear data",153variant: "destructive",154});155} finally {156setLoading(false);157}158};159160const testApiConnection = async () => {161try {162setLoading(true);163164// First test a simple endpoint165const testResponse = await fetch("/api/webhook/test", {166method: "POST",167headers: {168"Content-Type": "application/json",169},170body: JSON.stringify({ test: "data" }),171});172173if (testResponse.ok) {174// toast({175// title: "Test API Connection Successful",176// description: "The API is responding correctly",177// });178179// Now test sending some sample data to our webhook180const sampleData = {181data: {182camera_id: "test-camera",183filename: "test.jpg",184results: [185{186plate: "ABC1D23",187box: { xmin: 0, ymin: 0, xmax: 100, ymax: 50 },188vehicle: {189type: "Car",190box: { xmin: 0, ymin: 0, xmax: 200, ymax: 150 },191score: 0.95,192},193color: [{ color: "black", score: 0.9 }],194model_make: [{ make: "Test", model: "Model", score: 0.8 }],195direction: 90,196speed: 60,197score: 0.95,198source_url: "test-url",199},200],201timestamp: new Date().toISOString(),202timestamp_local: new Date().toISOString(),203timestamp_camera: new Date().toISOString(),204},205hook: {206event: "test",207filename: "test.jpg",208id: "test-id",209target: `/api/webhook/${uuid}`,210},211receivedAt: new Date().toISOString(),212};213214const webhookResponse = await fetch(`/api/webhook/${uuid}`, {215method: "POST",216headers: {217"Content-Type": "application/json",218},219body: JSON.stringify(sampleData),220});221222if (webhookResponse.ok) {223toast({224title: "Test Data Sent Successfully",225description:226"Sample license plate data has been sent to your webhook",227});228229// Refresh to show the test data230handleRefresh();231}232} else {233toast({234title: "API Connection Failed",235description: `Status: ${testResponse.status} ${testResponse.statusText}`,236variant: "destructive",237});238}239} catch (error) {240console.error("Error testing API connection:", error);241toast({242title: "API Connection Error",243description: error instanceof Error ? error.message : "Unknown error",244variant: "destructive",245});246} finally {247setLoading(false);248}249};250251// Get the latest vehicle data252const latestVehicle =253webhookData.length > 0254? webhookData.sort((a, b) => {255const timeA = a.receivedAt ? new Date(a.receivedAt).getTime() : 0;256const timeB = b.receivedAt ? new Date(b.receivedAt).getTime() : 0;257return timeB - timeA;258})[0]259: null;260261return (262<Layout263header={<Header />}264sidebar={265<LicensePlateSidebar266data={webhookData}267searchTerm={searchTerm}268onSearchChange={setSearchTerm}269/>270}271main={272<div className="space-y-4 overflow-auto">273{/* Dashboard Summary at the top */}274<DashboardSummary275data={webhookData}276onRefresh={handleRefresh}277onTestData={testApiConnection}278loading={loading}279/>280281{/* Webhook URL Display */}282<div className="bg-white p-4 rounded-lg shadow">283<h2 className="text-lg font-semibold mb-2">Webhook URL</h2>284<div className="flex items-center gap-2">285<div className="bg-gray-100 h-10 px-3 flex items-center rounded flex-1 font-mono text-sm overflow-x-auto">286{baseUrl ? `${baseUrl}/api/webhook/${uuid}` : `Loading...`}287</div>288<button289onClick={() => {290if (baseUrl) {291navigator.clipboard.writeText(292`${baseUrl}/api/webhook/${uuid}`,293);294toast({295title: "Copy!",296description:297"URL of the webhook copied to the clipboard.",298});299}300}}301className="h-10 w-10 flex items-center justify-center bg-blue-100 text-blue-700 rounded hover:bg-blue-200 transition-colors"302title="Copiar URL"303>304<Copy className="w-4 h-4" />305</button>306<button307onClick={handleDataClear}308className="h-10 w-10 flex items-center justify-center bg-red-100 text-red-600 rounded hover:bg-red-200 transition-colors"309title="Limpar Dados"310>311<Trash className="w-4 h-4" />312</button>313</div>314</div>315316{/* Main content area */}317{loading && webhookData.length === 0 ? (318<div className="flex items-center justify-center p-8">319<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>320<span className="ml-2">Loading data...</span>321</div>322) : webhookData.length === 0 ? (323<EmptyState uuid={uuid} />324) : (325<div className="space-y-4">326{/* Latest vehicle details */}327{latestVehicle && <LastVehicleDetails data={latestVehicle} />}328</div>329)}330</div>331}332/>333);334}335336337